Technote 1168The Care And Feeding of Runtime.exec |
CONTENTSThe Platform-Dependent Perils of |
R untime.exec( ) is probably the single least
cross-platform-compatible part of the Java API set. It assumes the existence of a
command-line interface to the OS and the ability to launch arbitrary apps that can
accept arbitrary parameters. Nevertheless, there are times when you need to use it
-- for instance, to open a URL in a Web browser or to spawn a new Java process. This
technote describes MRJ 2.x's implementation of Runtime.exec , and how
it differs from that of the JDK. |
The Platform-Dependent Perils of
|
The Path To The AppThe Mac OS has no search path, so you must specify an application using a real
file path. For example, just referring to the application as " Relative pathIf the application you're launching is at a known location relative to the application running (i.e., both are part of the same installation) you can provide a path that's relative to the current application. This works reliably only if you're running an application built with JBindery, not if you run your code directly from JBindery itself. User preferenceIf the app to launch is not part of your installation (for instance, if it's something standard like SimpleText or a web browser), you'll need to provide an absolute path. The most reasonable cross-platform way to do this is to store the path in a preference file. If the preference doesn't exist yet, or if you tried using the preferred path and it failed, you should put up a dialog box and ask the user to select the application, then store the path into the preference file. See Technote 1134, "The Preferences Problem," for more discussion about setting the correct path using a preference file. Lookup by creatorIf you're willing to use some Mac-specific code, it's friendlier to use the Mac OS facility to locate an application automatically given its creator code, which is a unique four-letter code assigned to that application. You can find the creator of any application by using ResEdit's
The MRJToolkit library, provided as part of the MRJ SDK (including extensive documentation)
includes a
Let JConfig locate it for youThe third-party JConfig
library from Samizdat Productions, described above, has features that can locate
the user's preferred helper app for any type of URL, which is one of the common uses
of Let OpenURL do it allIn MRJ 2.2, there is a new utility method
|
But I Came Here For An Argument!The Mac OS uses a mechanism known as AppleEvents to send messages from one process to another. AppleEvents are much higher level than command-line arguments, and include a rich set of data types and tags. However, this means that MRJ can't take a set of arbitrary argument strings and send them to an application in any meaningful way. (Remember all the stuff about "platform-dependent" in the first section?) MRJ tries to detect certain common types of command-line arguments and convert them into AppleEvents. In general, the two types of arguments MRJ knows about are file paths and URLs. Any argument containing a ":" character is interpreted as a URL, and anything else is interpreted as a file path. (These rules don't apply when you're launching another Java process, though. See the next section.)
(If you're AppleEvent savvy, you may wish to know that file paths are sent in
a single There are a couple of things to keep in mind: No multi-launchingThe Mac OS does not allow the same app to be launched multiple times, so if you
call URLs usually don't open a new windowThe convention in web browsers, at least, is that they display most URLs in the active window, replacing its previous contents, rather than opening a new window. (They do open files in new windows.) That means that if you send a browser a URL argument it will effectively ignore any argument that came before it. (For instance, if you pass it two URLs, it will try to show the first one and then immediately the second one in the same window. If you pass a file and a URL, it will open a new window for the file, but then show the URL in that window.) |
Launching JavaIt turns out that one of the useful things you can do with In MRJ 2.1 and 2.2, we valiantly added some special cases (some say "hacks")
to facilitate this. If the application/command named in the first argument does not
point to an existing file, and it does include the substring "
For example, you can invoke the Java compiler thusly:
Since we know the thing on the other end is a Java process, we allow a bit more general functionality:
Special flags for '
|
Flag |
Purpose |
MRJ |
MRJ 2.2 with |
---|---|---|---|
-help |
Prints help message & exits | Supported | Supported |
-classpath |
Sets class search path* | Supported | Supported |
-D |
Sets system property | Supported | Supported |
-Xverify:remote |
Enable bytecode verification on remote classes | Supported | Supported |
-Xverify:all |
Enable bytecode verification on all classes | Supported | Supported |
-Xverify:none |
Disable bytecode verification | Supported | Supported |
-Xnoasyncgc |
Disable asynchronous garbage collection | Ignored | Supported |
-Xnoclassgc |
Disable class GC | Ignored | Supported |
-Xms |
Set initial Java heap size | Ignored | Supported |
-Xmx |
Set max Java heap size | Ignored | Supported |
-Xss |
Set max native stack size | Ignored | Warning |
-Xoss |
Set max Java stack size | Ignored | Warning |
-Xverbose:gc |
Report on garbage collection | Ignored | Supported |
-Xverbose:jni |
Reports Java Native Interface activity | Ignored | Warning |
-Xverbose:class |
Reports class loading information | Ignored | Supported |
-verbose |
Reports class loading information | Ignored | Supported |
-X |
Prints help on nonstandard options & exits | Ignored | Warning |
-Xdebug |
Enable debugging | Ignored | Warning |
-Xprof |
Enable method profiling | Ignored | Supported |
-Xiprof |
Enable instruction profiling | Ignored | Warning |
-Xhprof |
Enable heap profiling | Ignored | Warning |
-version |
Prints version & exits | Error | Warning |
-cp |
Sets classpath | Error | Warning |
-jar |
Sets classpath and main | Error | Warning |
-XMRJAppBuilder |
Run via MRJAppBuilder app | Ignored | Required |
Future versions of MRJ may support more of these flags.
Note: In MRJ 2.2, the -XMRJAppBuilder option which tells MRJ to build an MRJAppBuilder
application on-the-fly instead of a JBindery application. If you do not use -XMRJAppBuilder ,
you will get the same functionality as MRJ 2.1. |
* A note on -classpath : As in JDK 1.2, you only need to specify additions
to the regular system classpath -- you do not need to add classes.zip
or JDKClasses.zip or rt.jar , and you'll get errors if you
do. Classpath entries can be absolute paths, or can be relative to the current working
directory, which by default is the directory containing the current application.When using -XMRJAppBuilder under MRJ 2.2, you may use the magic classpath entry $BOOTCLASSPATH
at the end of the classpath to denote that the classpath additions should be before
the regular system classpath. See MRJAppBuilder documentation for more details. |
Note: On MRJ 2.2 when using -XMRJAPPBuilder , console I/O streams to the child
process are not supported. If you are not using the Process object, then this is
a feature that improves launch time and removes the requirement for the machine be
configured for IP. But, if you want to use the Process object to communicate with
the child process, you should not use -XMRJAPPBuilder . Future versions of MRJ may add an explicit command line option to independently control whether or not the Process object sets up sockets to the child process. |
import java.awt.Frame; import java.awt.FileDialog; import java.io.File; import java.io.IOException; import com.apple.mrj.MRJFileUtils; public class ExecTest extends Frame { public static void main(String[ ] args) { new ExecTest(); System.exit(0); } public ExecTest() { String url = "http://developer.apple.com/java/"; try { //Attempt to let MRJ do all the work for us. MRJFileUtils.openURL(url); //If this was successful, then we need not go on. return; } catch (IOException exc) { //This can occur if problems arise while attempting //to open the URL. } catch (NoSuchMethodError err) { //This can occur when earlier versions of MRJ are used which //do not support the openURL method. } catch (NoClassDefFoundError err) { //This can occur under runtime environments other than MRJ. } //If we make it here, MRJ was unsuccessful in opening the URL, and //we need to do it the hard way, using Runtime.exec. String browserName; //Set up a FileDialog for the user to locate the browser to use. FileDialog fileDialog = new java.awt.FileDialog(this); fileDialog.setMode(FileDialog.LOAD); fileDialog.setTitle("Choose the browser to use:"); fileDialog.setVisible(true); //Retrieve the path information from the dialog and verify it. String resultPath = fileDialog.getDirectory(); String resultFile = fileDialog.getFile(); if(resultPath != null && resultPath.length()!= 0 && resultFile != null && resultFile.length() != 0) { File file = new File(resultPath + resultFile); if(file != null) { browserName = file.getPath(); try { //Launch the browser and pass it the desired URL Runtime.getRuntime().exec(new String[] {browserName, url}); } catch (IOException exc) { exc.printStackTrace(); } } } } } |
This example should be refined, if used in a real setting, to store the location
of the browser ( Alternatively, as described above, you could locate a browser by its application signature, or use JConfig or BrowserLauncher to handle the whole open-the-URL process for you. |
![]() |